effective C++(1)

  01.C++总体来说包含四大部分内容,分别是:C语言部分(没有模板、异常、重载等概念);面向对象部分(类、封装、继承、多态、虚函数等);泛型编程部分(模板技术);STL(容器、算法、迭代器等)。
  02.对单纯常量,尽量以const对象或enums代替#define,因为宏定义是预处理器干的活,宏定义的记号名称有可能未被编译器看到,也许在编译器开始处理源码之前它就被预处理器移走了,因此编译会出错。而如果使用const等定义一个常量,编译器是一定会看到的,当然就会进入记号表内。
  03.对于class专属常量,为了将常量的作用域限制于class内,必须让该常量成为class的一个成员。而为确保此常量至多只有一份实体,必须让它成为一个static成员。如下:

1
2
3
4
5
6
7
8
class A
{
private:
static const int Num = 5; //常量声明式
int scores[Num];
...
};
const int A::Num; //Num的定义

  上面程序中,在声明的时候已经给常量做了初始化,对于class的static类型的专属常量,只要不取它们的地址,或者不需要用到定义式,则不提供定义式也可以,因为在声明的时候已经给了初值,所以在定义式中不可以再设初值。
  04.对于形似函数的宏,最好用inline函数替换#define。
  05.const修饰指针的时候,如果const出现在星号左边,表示被指物是常量,如果出现在星号右边,表示指针自身是常量,如果出现在星号两边,表示被指物和指针两者都是常量。注意:当被指物是常量时,const写在类型前和类型后都可以。
  06.声明STL的迭代器为const和声明指针为const是一样的操作,但是声明迭代器所指物为const需要用const_iterator。
  07.non-const成员函数可以调用const成员函数,反之则不行。
  08.确保对象在使用之前已经被初始化,读取未初始化的值会导致不明确的行为。
  09.对于大多数类型而言,比起先调用默认构造函数后再调用拷贝赋值操作符,单只调用一次拷贝构造函数是比较高效的。对于内置类型来说,其初始化和赋值的成本相同,但为了一致性,最好也通过初始值列表来初始化。
  10.如果成员变量是const或引用,则必须初始化,不能被赋值。
  11.成员变量的初始化顺序是以其声明顺序被初始化的,并且父类的变量先初始化,再子类。
  12.如果某编译单元内的某个全局static对象的初始化使用了另一编译单元内的某个全局static对象,它所用到的这个对象可能尚未被初始化,因为C++对“定义于不同编译单元内的全局static对象”的初始化次序并无明确定义。为解决此问题,可以用一个函数来代替这个全局对象。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//有问题的代码
class FileSystem
{
public:
...
std::size_t num() const;
...
};
extern FileSystem tfs; //给客户使用的对象

class Directory
{
public:
  Directory(params);
  ...
};
Directory::Directory(params)
{
  ...
  std::size dis = tfs.num(); //使用tfs对象
...
}
Directory tempDir(params);

//修改后的代码
class FileSystem
{
public:
...
std::size_t num() const;
...
};
FileSystem& tfs() //用该函数替换tfs对象
{
static FileSystem fs;
return fs;
}
class Directory
{
public:
  Directory(params);
  ...
};
Directory::Directory(params)
{
  ...
  std::size dis = tfs().num(); //改用tfs()
...
}
Directory& tempDir() //用该函数替换tempDir对象
{
static Directory td;
return td;
}